﻿///***************************************************************
/// 시  스  템 : SMFramework
/// 단위시스템 : Util
/// 계      층 : Framework Utility
/// 프로그램명 : DataSetUtil
/// 파  일  명 : DataSetUtil.cs
/// 설      명 : DataSet관련 유틸리티
///***************************************************************
/// 수정ID 일자       이름     내용
///---------------------------------------------------------------
/// CH001  2008-07-08 송시명   최초 작성
///***************************************************************
///
using System;
using System.Data;

namespace SMFramework.Utility
{
	/// <summary>
	/// DataSet관련 유틸리티
	/// </summary>
    public sealed class DataSetUtil
	{
		private DataSetUtil()
		{
		}

		#region CheckTable, CheckField, CheckRowCount
		/// <summary>
		/// DataSet에 테이블이 있는지 체크
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <returns></returns>
		public static bool CheckTable(DataSet ds, string tablename)
		{
			return ds.Tables.Contains(tablename);
		}

		/// <summary>
		/// DataSet의 테이블에 필드가 있는지 체크
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="fieldname"></param>
		/// <returns></returns>
		public static bool CheckField(DataSet ds, string tablename, string fieldname)
		{
			if (CheckTable(ds, tablename))
			{
				return ds.Tables[tablename].Columns.Contains(fieldname);
			}
			else
			{
				return false;
			}
		}

		/// <summary>
		/// 주어진 테이블의 결과가 있는지 체크
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <returns></returns>
		public static bool CheckRowCount(DataSet ds, string tablename)
		{
			if (CheckTable(ds, tablename))
			{
				if (ds.Tables[tablename].Rows.Count > 0)
				{
					return true;
				}
				else
				{
					return false;
				}
			}
			else
			{
				return false;
			}
		}
		#endregion

		#region GetRowCount, GetColCount
		/// <summary>
		/// 주어진 테이블의 Row수 리턴
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <returns></returns>
		public static int GetRowCount(DataSet ds, string tablename)
		{
			if (CheckTable(ds, tablename))
			{
				if (ds.Tables[tablename].Rows.Count > 0)
				{
					return ds.Tables[tablename].Rows.Count;
				}
				else
				{
					return 0;
				}
			}
			else
			{
				return -1;
			}
		}

		/// <summary>
		/// 주어진 테이블의 Column수 리턴
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <returns></returns>
		public static int GetColCount(DataSet ds, string tablename)
		{
			if (CheckTable(ds, tablename))
			{
				if (ds.Tables[tablename].Columns.Count > 0)
				{
					return ds.Tables[tablename].Columns.Count;
				}
				else
				{
					return 0;
				}
			}
			else
			{
				return -1;
			}
		}
		#endregion

		#region GetFldStr
		/// <summary>
		/// 데이터셋에서 테이블, 필드로 string 값 가져옴
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="fieldname"></param>
		/// <returns></returns>
		public static string GetFldStr(DataSet ds, string tablename, string fieldname)
		{
			return GetFldStr(ds, tablename, 0, fieldname);
		}

		/// <summary>
		/// 데이터셋에서 테이블, 인자, 필드로 string 값 가져옴
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="idx"></param>
		/// <param name="fieldname"></param>
		/// <returns></returns>
		public static string GetFldStr(DataSet ds, string tablename, int idx, string fieldname)
		{
			if (CheckTable(ds, tablename))
			{
				if (ds.Tables[tablename].Columns.Contains(fieldname))
				{
					return ds.Tables[tablename].Rows[idx][fieldname].ToString().Trim();
				}
				else
				{
					return "";
				}
			}
			else
			{
				return "";
			}
		}
		#endregion

		#region GetFldInt
		/// <summary>
		/// 데이터셋에서 테이블, 필드로 int 값 가져옴
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="fieldname"></param>
		/// <returns></returns>
		public static int GetFldInt(DataSet ds, string tablename, string fieldname)
		{
			return GetFldInt(ds, tablename, 0, fieldname);
		}

		/// <summary>
		/// 데이터셋에서 테이블, 인자, 필드로 int 값 가져옴
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="idx"></param>
		/// <param name="fieldname"></param>
		/// <returns></returns>
		public static int GetFldInt(DataSet ds, string tablename, int idx, string fieldname)
		{
			try
			{
				if (CheckTable(ds, tablename))
				{
					if (ds.Tables[tablename].Columns.Contains(fieldname))
					{
						return Convert.ToInt32(ds.Tables[tablename].Rows[idx][fieldname].ToString().Trim());
					}
					else
					{
						return 0;
					}
				}
				else
				{
					return 0;
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}
		#endregion

		#region GetFldDouble
		/// <summary>
		/// 데이터셋에서 테이블, 필드로 double 값 가져옴
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="fieldname"></param>
		/// <returns></returns>
		public static double GetFldDbl(DataSet ds, string tablename, string fieldname)
		{
			return GetFldDbl(ds, tablename, 0, fieldname);
		}

		/// <summary>
		/// 데이터셋에서 테이블, 인자, 필드로 double 값 가져옴
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="idx"></param>
		/// <param name="fieldname"></param>
		/// <returns></returns>
		public static double GetFldDbl(DataSet ds, string tablename, int idx, string fieldname)
		{
			try
			{
				if (CheckTable(ds, tablename))
				{
					if (ds.Tables[tablename].Columns.Contains(fieldname))
					{
						return Convert.ToDouble(ds.Tables[tablename].Rows[idx][fieldname].ToString().Trim());
					}
					else
					{
						return 0.00;
					}
				}
				else
				{
					return 0.00;
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}		
		#endregion

		#region AddTable, AddField, AddRow
		/// <summary>
		/// DataSet에 테이블을 추가함
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <returns></returns>
		public static bool AddTable(DataSet ds, string tablename)
		{
			if (CheckTable(ds, tablename))
			{
				return false;
			}
			else
			{
				ds.Tables.Add(tablename);
				return true;
			}
		}

		/// <summary>
		/// DataSet의 테이블에 필드를 추가함(기본형을 string으로 추가)
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="fieldname"></param>
		/// <returns></returns>
		public static bool AddField(DataSet ds, string tablename, string fieldname)
		{
			return AddField(ds, tablename, fieldname, typeof(System.String));
		}

		/// <summary>
		/// DataSet의 테이블에 필드를 추가함
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="fieldname"></param>
		/// <param name="typename"></param>
		/// <returns></returns>
		public static bool AddField(DataSet ds, string tablename, string fieldname, Type typename)
		{
			if (!CheckTable(ds, tablename))
			{
				AddTable(ds, tablename);
			}
			if (CheckField(ds, tablename, fieldname))
			{
				return false;
			}
			else
			{
				ds.Tables[tablename].Columns.Add(fieldname, typename);
				return true;
			}
		}

		/// <summary>
		/// 빈 데이터셋에 빈 Row를 추가함
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="tablename"></param>
		/// <param name="row"></param>
		/// <returns></returns>
		public static DataSet AddRow(DataSet ds, string tablename, object[] row)
		{
			DataSet oDataSet = new DataSet();
			DataRow drRow = null;
			DataTable customDt = new DataTable();

			DataColumn colReaderName = new DataColumn("ReaderName");
			colReaderName.DataType = System.Type.GetType("System.String");
			customDt.Columns.Add(colReaderName);			
			
			drRow = oDataSet.Tables[0].NewRow();

			drRow["ReaderName"] = "TEST";

			oDataSet.Tables[0].Rows.Add(drRow);
			
			return oDataSet;
		}
		#endregion

		#region GetArrayFromDataSet
		/// <summary>
		/// 데이터셋의 특정 필드의 값을 배열로 리턴
		/// </summary>
		/// <param name="po_ds"></param>
		/// <param name="ps_table_name"></param>
		/// <param name="ps_field_name"></param>
		/// <returns></returns>
		public static string[] GetArrayFromDataSet(DataSet po_ds, string ps_table_name, string ps_field_name)
		{
			string ls_data = "";

			if (DataSetUtil.CheckRowCount(po_ds, ps_table_name))
			{
				for (int i = 0; i < DataSetUtil.GetRowCount(po_ds, ps_table_name); i++)
				{
					ls_data += DataSetUtil.GetFldStr(po_ds, ps_table_name, i, ps_field_name) + ",";
				}
			}
			else
			{
				ls_data = ",";
			}
			string[] la_rslt = ls_data.Substring(0, ls_data.LastIndexOf(",")).Split(',');

			return la_rslt;
		}
		#endregion

		#region GetStringFromDataSet
		/// <summary>
		/// 데이터셋의 특정 필드의 값을 하나의 문자열로 리턴 (구분자는 파이프라인)
		/// </summary>
		/// <param name="po_ds"></param>
		/// <param name="ps_table_name"></param>
		/// <param name="ps_field_name"></param>
		/// <returns></returns>
		public static string GetStringFromDataSet(DataSet po_ds, string ps_table_name, string ps_field_name)
		{
			return GetStringFromDataSet(po_ds, ps_table_name, ps_field_name, "|");
		}

		/// <summary>
		/// 데이터셋의 특정 필드의 값을 하나의 문자열로 리턴 (지정한 구분자로 구분)
		/// </summary>
		/// <param name="po_ds"></param>
		/// <param name="ps_table_name"></param>
		/// <param name="ps_field_name"></param>
		/// <param name="delimiter"></param>
		/// <returns></returns>
		public static string GetStringFromDataSet(DataSet po_ds, string ps_table_name, string ps_field_name, string delimiter)
		{
			string ls_data = "";

			if (DataSetUtil.CheckRowCount(po_ds, ps_table_name))
			{
				for (int i = 0; i < DataSetUtil.GetRowCount(po_ds, ps_table_name); i++)
				{
					ls_data += DataSetUtil.GetFldStr(po_ds, ps_table_name, i, ps_field_name) + delimiter;
				}
			}

			return ls_data.Substring(0, ls_data.LastIndexOf(delimiter));
		}

		
		#endregion

		#region MakeSubTotal
		/// <summary>
		/// MakeSubTotal
		/// </summary>
		/// <param name="dt">DataTable</param>
		/// <param name="lstSum">SubTotal대상 컬럼의 인덱스값 배열</param>
		/// <param name="StartValueColumnPos">계산시작 컬럼인덱스</param>
		/// <returns></returns>
		public static DataTable MakeSubTotal(DataTable dt, string[] lstSum, int StartValueColumnPos)
		{
			double RoundValue = 0.0009;
			double RoundValueMinus = -0.0009;

			// 결과치가 담길 테이블.. 
			DataTable desc = new DataTable();

			// 로우데이타 구분이 담길 컬럼..
			int iColumnType = dt.Columns.Count;

			// 토탈값을 유지할 배열
			double[] Total = new double[dt.Columns.Count - StartValueColumnPos];

			// 서브 토탈값을 유지할 배열
			double[,] SubTotalValue = new double[lstSum.Length, dt.Columns.Count - StartValueColumnPos];

			// 서브토탈의 기준이 되는 값
			string[] SubTotal = new string[lstSum.Length];

			DataRow rows = null;

			// 부분 합계용 데이타 row
			DataRow[] subTotalRows = new DataRow[lstSum.Length];

			// 부분합계를 낼때 상단의 부분합계가 바뀌면 무조건 하단의 부분합계를 바꾸기위해.
			bool isSuperChanged;

			// 데이타 타입을 저장해놓을 장소
			string[] DataTypes = new string[dt.Columns.Count];

			// 임시 변수  RoundValue 이하의 값은 버리기 위하여
			double temp = 0;

			// 컬럼 타입 생성
			for (int i = 0; i < dt.Columns.Count; i++)
			{
				desc.Columns.Add(dt.Columns[i].ColumnName, dt.Columns[i].DataType);
				DataTypes[i] = dt.Columns[i].DataType.Name.ToUpper();
			}

			// 합계인지 로우 데이타인지 구분하기 위한 플래그..
			// 로우데이타는 1
			// 합계는 2
			// 각각의 SubTotal 은 +1 씩
			desc.Columns.Add("iDataRowType", typeof(int));

			// 서브토탈 값비교를 위한 값 미리 셋팅. 
			if (dt.Rows.Count > 0)
			{
				for (int i = 0; i < lstSum.Length; i++)
				{
					if (lstSum[i].ToString().ToUpper() == "TRUE")
						SubTotal[i] = (dt.Rows[0][i] == null ? null : dt.Rows[0][i].ToString());
				}
			}

			// 전체 row 을 순회함.. 
			for (int row = 0; row < dt.Rows.Count; row++)
			{
				rows = desc.NewRow();

				isSuperChanged = false;

				// 전체 컬럼을 순회함.. 
				for (int column = 0; column < dt.Columns.Count; column++)
				{
					// 부분합계를 내기 위하여 이전것과 같은지 비교
					if (column >= 0 && column < (lstSum.Length))
					{
						// 자기자신이 틀리거나 상위의 부분합계가 변경된경우  하위에것은 무조건 변경. 
						if (SubTotal[column] != (dt.Rows[row][column] == null ? null : dt.Rows[row][column].ToString()) || isSuperChanged)
						{
							if (lstSum[column].ToString().ToUpper() == "TRUE")
							{
								subTotalRows[column] = desc.NewRow();

								// 값을 서브토탈 row 에 복사하고 다시 누적 시작.. 하기 위하여 0 으로 초기화
								for (int subValue = 0; subValue < (dt.Columns.Count - StartValueColumnPos); subValue++)
								{
									// 서브토탈이 숫자인경우만 더해줌.. 
									if (DataTypes[StartValueColumnPos + subValue] == "INT" || 
										DataTypes[StartValueColumnPos + subValue] == "DOUBLE" ||
										DataTypes[StartValueColumnPos + subValue] == "INT32" || 
										DataTypes[StartValueColumnPos + subValue] == "DECIMAL")
									{
										subTotalRows[column][StartValueColumnPos + subValue] = SubTotalValue[column, subValue];
									}

									SubTotalValue[column, subValue] = 0;
								}

								// 부분합계 이전 값들을 체움. Merge 를 위해서. 
								if (row != 0)
								{
									for (int i = 0; i < column + 1; i++)
									{
										subTotalRows[column][i] = dt.Rows[row - 1][i];
									}
								}
								subTotalRows[column][column] = SubTotal[column] + " Sub Total";

								SubTotal[column] = (dt.Rows[row][column] == null ? null : dt.Rows[row][column].ToString());

								isSuperChanged = true;
							}
						}
					}

					// StartValueColumnPos 이상의 컬럼들만 누적해나감.. 
					// 토탈및 서브토탈 값을 계산함..
					if (column >= StartValueColumnPos)
					{
						// 숫자 타입인 서브토탈을 계산함
						if (DataTypes[column] == "INT" || 
							DataTypes[column] == "DOUBLE" || 
							DataTypes[column] == "INT32" || 
							DataTypes[column] == "DECIMAL")
						{
							// 토탈값 계산..
							if (dt.Rows[row][column] != null && dt.Rows[row][column] != DBNull.Value)
							{
								Total[column - StartValueColumnPos] += Convert.ToDouble(dt.Rows[row][column]);

								//서브 토탈을 누적함. 
								for (int sub = 0; sub < SubTotal.Length; sub++)
								{
									if (lstSum[sub].ToString().ToUpper() == "TRUE")
									{
										SubTotalValue[sub, column - StartValueColumnPos] += Convert.ToDouble(dt.Rows[row][column]);
									}
								}
							}
						}
					}

					// 일반 low data 복사
					// Double 타입은 속도를 위해서 0.009 이하의 값들은 버리고 null 값으로 셋팅함.
					if (dt.Rows[row][column] != null && dt.Rows[row][column] != DBNull.Value)
					{
						if (DataTypes[column] == "DOUBLE")
						{
							temp = Convert.ToDouble(dt.Rows[row][column]);
							if (temp < RoundValue && temp > RoundValueMinus)
							{
								rows[column] = 0;//DBNull.Value;
							}
							else
							{
								rows[column] = dt.Rows[row][column];
							}
						}
						else
						{
							rows[column] = dt.Rows[row][column];
						}
					}
					else
					{
						rows[column] = dt.Rows[row][column];
					}
				}

				// 부분합계열 추가.. 
				// 역순으로 넣어야만.. 됨.. 
				for (int sub = subTotalRows.Length - 1; sub >= 0; sub--)
				{
					if (lstSum[sub].ToString().ToUpper() == "TRUE")
					{
						if (subTotalRows[sub] != null)
						{
							// 데이타 타입을 입력함.. 
							// 로우데이타는 1
							// 합계는 2
							// 각각의 SubTotal 은 +1 씩, 첫 부분합계 값은 3 부터..
							subTotalRows[sub][iColumnType] = sub + 3;
							desc.Rows.Add(subTotalRows[sub]);
							subTotalRows[sub] = null;
						}
					}
				}

				// 데이타 타입을 입력함.. 
				// 로우데이타는 1
				// 합계는 2
				// 각각의 SubTotal 은 +1 씩
				rows[iColumnType] = 1;

				desc.Rows.Add(rows);
			}

			// 마지막에 남아있는 부분합계 뒤에서부터 채움.. 
			for (int subValue = SubTotal.Length - 1; subValue >= 0; subValue--)
			{
				if (lstSum[subValue].ToString().ToUpper() == "TRUE")
				{
					DataRow subTotalRow = desc.NewRow();

					for (int col = StartValueColumnPos; col < dt.Columns.Count; col++)
					{
						if (DataTypes[col] == "INT" || DataTypes[col] == "DOUBLE" || DataTypes[col] == "INT32" || DataTypes[col] == "DECIMAL")
						{
							subTotalRow[col] = SubTotalValue[subValue, col - StartValueColumnPos];
						}
					}

					// 부분합계 이전 값들을 체움.
					if (dt.Rows.Count > 0)
					{
						for (int i = 0; i < (subValue + 1); i++)
						{
							subTotalRow[i] = dt.Rows[dt.Rows.Count - 1][i];
						}
					}

					// 데이타 타입을 입력함.. 
					// 로우데이타는 1
					// 합계는 2
					// 각각의 SubTotal 은 +1 씩, 첫 부분합계 값은 3 부터..
					subTotalRow[iColumnType] = subValue + 3;
					subTotalRow[subValue] = SubTotal[subValue] + " Sub Total";
					desc.Rows.Add(subTotalRow);
				}
			}

			// 토탈값
			DataRow TotalRow = desc.NewRow();

			// 토탈 추가함.. 
			desc.Rows.Add(TotalRow);

			// 토탈값을 채움.. 
			TotalRow[0] = "Total";

			// 데이타 타입을 입력함.. 
			// 로우데이타는 1
			// 합계는 2
			TotalRow[iColumnType] = 2;
			for (int i = StartValueColumnPos; i < dt.Columns.Count; i++)
			{
				// 숫자 타입인 서브토탈을 계산함
				if (DataTypes[i] == "INT" || DataTypes[i] == "DOUBLE" || DataTypes[i] == "INT32" || DataTypes[i] == "DECIMAL")
				{
					TotalRow[i] = Total[i - StartValueColumnPos];
				}
			}

			return desc;
		}
		#endregion
	}
}
